/**
 * @file demo_app.cpp
 */
#include <chrono>
#include <csignal>
#include <cstring>
#include <iostream>
#include <memory>
#include <thread>

#include "foo.h"
#include "nosal/demolib/context.h"
#include "nosal/demolib/register.h"

using namespace netaos::nosal::demo::lib;

namespace {

/**
 * @brief SigTermHandler SIGTERM handler
 */
void SigTermHandler(int);

/**
 * @brief IsSigTermRaised Getter for a flag "Is SIGTERM rased"
 * @return true if the process got SIG_TERM otherwise false
 */
bool IsSigTermRaised();

}  // namespace

/**
 * @brief main
 *
 * @param argc
 * @param argv
 *
 * @note argv[0] is a path to the executable binary
 * @note argv[1] defines "work time"; possible values:
 *               <empty> - endless work (till SIGTERM is raised)
 *               <number> - work time in milliseconds
 *
 * @return
 */
int main(int argc, char **argv) {
    std::cout << std::endl << "Starting process " << argv[0] << std::endl;

    Context ctx("TextCtx", "This is the context for test");
    RegisterContext(ctx);

    const bool                      is_self_terminating = (argc > 1);
    const std::chrono::milliseconds work_time(
        is_self_terminating ? std::stoul(argv[1]) : 0U);

    std::signal(SIGTERM, SigTermHandler);

    typedef std::conditional<std::chrono::high_resolution_clock::is_steady,
                             std::chrono::high_resolution_clock,
                             std::chrono::steady_clock>::type clock;

    foo();

    const auto startTime = clock::now();
    while ((!is_self_terminating || (clock::now() - startTime) < work_time) &&
           !IsSigTermRaised()) {

        // Using sleep step means that the process exit timeout should be bigger
        // than sleep step
        static const std::chrono::milliseconds kSleepStep(5U);

        std::this_thread::sleep_for(kSleepStep);
    }

    UnregisterContext(ctx);

    std::cout << std::endl << "Stopping process " << argv[0] << std::endl;
    return EXIT_SUCCESS;
}

namespace {

volatile std::sig_atomic_t gIsSigTermRaised = 0;

void SigTermHandler(int) {
    gIsSigTermRaised = 1;
}

bool IsSigTermRaised() {
    return gIsSigTermRaised;
}

}  // namespace
